package work.mediway.mdm.controller.common;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.mediway.hos.base.model.BaseResponse;
import com.mediway.hos.common.util.excel.ExcelUtils;
import com.mediway.hos.log.annotation.OperLog;

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import work.mediway.mdm.core.page.QueryRequest;
import work.mediway.mdm.biz.bo.DictPushBO;
import work.mediway.mdm.core.annotation.OpsLog;
import work.mediway.mdm.core.enums.DataFormatEnum;
import work.mediway.mdm.core.enums.OpsLogTypeEnum;
import work.mediway.mdm.core.transport.ErrorMsgVO;
import work.mediway.mdm.core.util.DUtil;
import work.mediway.mdm.core.util.HttpContextUtil;
import work.mediway.mdm.biz.dto.dict.DictFieldDTO;
import work.mediway.mdm.biz.dto.dict.DictGeneralDTO;
import work.mediway.mdm.biz.dto.dict.DictGeneralDeleteDTO;
import work.mediway.mdm.biz.dto.dict.DictListDTO;
import work.mediway.mdm.biz.entity.dict.CtCodeSystem;
import work.mediway.mdm.biz.entity.dict.DictEtlDTO;
import work.mediway.mdm.biz.helper.BusinessDatabaseHelper;
import work.mediway.mdm.biz.service.biz.TableFieldService;
import work.mediway.mdm.biz.service.common.CommonDictService;
import work.mediway.mdm.biz.service.dict.CtCodeSystemService;
import work.mediway.mdm.biz.vo.dict.DictFieldVO;
import work.mediway.mdm.biz.vo.dict.DictFormatTemplateVO;
import work.mediway.mdm.biz.vo.dict.DictListVO;

/**
 * 字典通用方法
 *
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2021/10/11
 */
@Api(tags = "字典通用方法模块")
@ApiSupport(order = 5)
@Validated
@RequestMapping("dict/general")
@RestController
@RequiredArgsConstructor
public class CommonDictGeneralController {

    private final CommonDictService commonDictService;
    private final CtCodeSystemService codeSystemService;
    private final BusinessDatabaseHelper businessDatabaseHelper;
    private final TableFieldService tableFieldService;

    @OpsLog(value = "通用字典保存/更新", type = OpsLogTypeEnum.INSERT)
    @OperLog(title = "字典信息-通用字典保存/更新", content = "通用字典保存/更新")
    @PostMapping
    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "通用字典保存/更新")
    @ApiImplicitParam(name = "xmlData", value = "xml类型数据", paramType = "query")
    public BaseResponse<ErrorMsgVO> saveDict(@RequestBody DictGeneralDTO dictGeneralDTO, String xmlData) {
        ErrorMsgVO exceptions = this.commonDictService.saveDictDetail(dictGeneralDTO, xmlData);
        return BaseResponse.success(exceptions);
    }

    @OpsLog(value = "数据采集字典保存/更新", type = OpsLogTypeEnum.INSERT)
    @PostMapping("etl")
    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "数据采集字典保存/更新")
    public BaseResponse<ErrorMsgVO> saveEtlDict(@RequestBody DictEtlDTO dictEtlDTO) {
        if (ObjectUtil.isEmpty(dictEtlDTO)) {
            return BaseResponse.error("数据采集未传入数据");
        }
        if (CollUtil.isEmpty(dictEtlDTO.getData())) {
            return BaseResponse.error(StrUtil.format("数据采集传入的数据为: {}", dictEtlDTO.getData()));
        }
        Object etlTableCode = dictEtlDTO.getData().get(0).get("tableCode");
        if (ObjectUtil.isEmpty(etlTableCode)) {
            return BaseResponse.error(StrUtil.format("数据采集未传入表信息, tableCode = {}", etlTableCode));
        }
        String tableCode = etlTableCode.toString();

        DictGeneralDTO dictGeneral = new DictGeneralDTO();
        DictGeneralDTO.RequestBean requestBean = new DictGeneralDTO.RequestBean();

        requestBean.setData(dictEtlDTO.getData());
        requestBean.setTableCode(tableCode);
        dictGeneral.setRequest(requestBean);
        ErrorMsgVO exceptions = this.commonDictService.saveDictDetail(dictGeneral, "");
        return BaseResponse.success(exceptions);
    }

    @OpsLog(value = "通用字典查询/导出", type = OpsLogTypeEnum.SELECT)
    @GetMapping
    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "通用字典查询/导出")
    @ApiImplicitParam(value = "是否导出成.xlsx的Excel文件", name = "export", paramType = "query", allowableValues = "false,true")
    public BaseResponse<Page<Map<String, Object>>> dictDetailShowOrExport(@Valid DictFieldDTO dictDTO, QueryRequest queryRequest, @RequestParam(defaultValue = "false") boolean export) {
        // 查询数据
        Page<Map<String, Object>> result = this.commonDictService.dictDetailShowOrExport(dictDTO, queryRequest, export, false);
        if (export) {
            exportDictData(dictDTO, result);
            return null;
        }
        return CollUtil.isNotEmpty(result.getRecords()) ? BaseResponse.success(result) : BaseResponse.error();
    }

    private void exportDictData(@Valid DictFieldDTO dictDTO, Page<Map<String, Object>> result) {
        // 根据ExcelUtil生成器文件全路径
        DictListDTO listDTO = new DictListDTO();
        listDTO.setDictParam(dictDTO.getTableCode());
        DictListVO codeSystem = this.codeSystemService.pageCodeSystem(listDTO).getRecords().get(0);

        List<LinkedHashMap<String, Object>> excelList = new ArrayList<>();

        // 查询数据库字段
        List<DictFieldVO> field = this.tableFieldService.listDictField(dictDTO, true);
        // 组装数据
        for (Map<String, Object> record : result.getRecords()) {
            LinkedHashMap<String, Object> strMap = new LinkedHashMap<>();
            for (DictFieldVO vo : field) {
                String value = String.valueOf(record.get(vo.getFieldCode()));
                if (StrUtil.isBlank(value) || StrUtil.equalsIgnoreCase(value, "null")) {
                    value = "";
                } else {
                    // 日期字段特殊处理
                    if (DUtil.isDate(value)) {
                        value = DateUtil.formatDate(DateUtil.parseDate(value));
                    }
                    if (DUtil.isTime(value)) {
                        value = DateUtil.formatTime(DateUtil.parseTime(value));
                    }
                }
                strMap.put(vo.getFieldDesc(), value);
            }
            excelList.add(strMap);
        }
        String fileName = codeSystem.getCodeSystemDesc() + StringPool.LEFT_BRACKET + codeSystem.getCodeSystemCode() + StringPool.RIGHT_BRACKET + "-" + IdUtil.getSnowflakeNextIdStr();
        try {
            ExcelUtils.exportXlsx(HttpContextUtil.getResponse(), fileName, excelList);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OpsLog(value = "通用字典删除(根据主键)", type = OpsLogTypeEnum.DELETE)
    @OperLog(title = "字典信息-通用字典删除(根据主键)", content = "通用字典删除(根据主键)")
    @PostMapping("delete")
    @ApiOperationSupport(order = 6)
    @ApiOperation(value = "通用字典删除(根据主键)")
    public BaseResponse<ErrorMsgVO> removeDict(@RequestBody DictGeneralDeleteDTO dictGeneralDeleteDTO) {
        ErrorMsgVO exceptions = this.commonDictService.removeDict(dictGeneralDeleteDTO);
        return BaseResponse.success(exceptions);
    }

    @OpsLog(value = "通用字典导入模板", type = OpsLogTypeEnum.UPLOAD)
    @GetMapping("excel/template")
    @ApiOperationSupport(order = 8)
    @ApiOperation(value = "通用字典导入模板")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "table", value = "表名", paramType = "query")
    })
    public void importDictTemplate(@NotBlank(message = "不能为空") String table) {
        List<LinkedHashMap<String, Object>> excelList = new ArrayList<>();

        // 查询数据库字段
        DictFieldDTO dictFieldDTO = new DictFieldDTO();
        dictFieldDTO.setTableCode(table);
        List<DictFieldVO> field = this.tableFieldService.listDictFieldAll(dictFieldDTO, true);
        LinkedHashMap<String, Object> strMap = new LinkedHashMap<>();
        field.forEach(item -> {
            if (BooleanUtil.isTrue(item.getRequired())) {
                strMap.put("(必需)" + item.getFieldDesc() + "-" + item.getFieldCode(), item.getFieldDesc());
            } else if (StrUtil.isNotBlank(item.getFkColumnName())) {
                strMap.put("(外键)" + item.getFieldDesc() + "-" + item.getFieldCode() + "-(表" + item.getFkTableName() + "的" + item.getFkColumnName() + ")", item.getFieldDesc());
            } else {
                strMap.put(item.getFieldDesc() + "-" + item.getFieldCode(), item.getFieldDesc());
            }
        });
        excelList.add(strMap);
        String fileName = table + "-" + IdUtil.getSnowflakeNextIdStr();
        try {
            ExcelUtils.exportXlsx(HttpContextUtil.getResponse(), fileName, excelList);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OpsLog(value = "字典数据存储请求", type = OpsLogTypeEnum.SELECT)
    @GetMapping("template")
    @ApiOperationSupport(order = 9)
    @ApiOperation(value = "字典数据存储请求")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "table", value = "表名", paramType = "query"),
            @ApiImplicitParam(name = "format", value = "数据格式", paramType = "query", allowableValues = "XML,JSON")
    })
    public BaseResponse<DictFormatTemplateVO> saveDictTemplate(String table, DataFormatEnum format) {
        DictFormatTemplateVO dictSaveTemplate = this.commonDictService.buildDictTemplate(table, format);
        return BaseResponse.success(dictSaveTemplate);
    }

    @OpsLog(value = "通用字典导入", type = OpsLogTypeEnum.UPLOAD)
    @OperLog(title = "字典信息-通用字典导入", content = "通用字典导入")
    @PostMapping("import")
    @ApiOperationSupport(order = 10)
    @ApiOperation(value = "通用字典导入")
    public BaseResponse<ErrorMsgVO> importDict(MultipartFile file) throws IOException {
        ExcelReader excelReader = ExcelUtil.getReader(file.getInputStream());
        ErrorMsgVO exceptions = this.commonDictService.importDict(excelReader);
        return BaseResponse.success(exceptions);
    }

    @OpsLog(value = "字典数据推送", type = OpsLogTypeEnum.PUSH)
    @OperLog(title = "字典信息-字典数据推送", content = "字典数据推送")
    @PostMapping("push")
    @ApiOperationSupport(order = 12)
    @ApiOperation(value = "字典数据推送")
    public BaseResponse<String> pushDict(@RequestBody DictPushBO dictPushBO) {
        this.commonDictService.pushDict(dictPushBO);
        return BaseResponse.success();
    }

    @OpsLog(value = "获取主数据字典的所有表主键", type = OpsLogTypeEnum.SELECT)
    @GetMapping("table/primary")
    @ApiOperationSupport(order = 14)
    @ApiOperation(value = "获取主数据字典的所有表主键")
    public List<Map<String, String>> getAllTablePrimary() {
        List<Map<String, String>> result = CollUtil.newArrayList();
        List<CtCodeSystem> codeSystems = this.codeSystemService.list();
        for (CtCodeSystem codeSystem : codeSystems) {
            Map<String, String> data = MapUtil.newHashMap();
            List<String> primaryKey = this.businessDatabaseHelper.getPrimaryKey(codeSystem.getCodeSystemCode());
            if (CollUtil.isNotEmpty(primaryKey)) {
                data.put(codeSystem.getCodeSystemCode(), primaryKey.get(0));
                result.add(data);
            }
        }
        return result;
    }

}
